#include "Ray.h"
#include "Scene.h"
#include <math.h>
#include <limits>

Color Ray::trace(Scene* myScene, int &itemFound){
	if(recursionDepth == MAX_RECURSION){
		return Color();
	}
	else{
		double closestT = std::numeric_limits<double>::max();
		Geometry* selectedObject = NULL;
		for(size_t i = 0; i < myScene->objects.size(); i++){
			std::vector<double> solutions = myScene->objects[i]->intersection(*this);
			for(size_t j = 0; j < solutions.size(); j++){
				if(recursionDepth == 0){ 
					// The primary ray must have a t > 1 in order to be past the
					// viewport.
					if(solutions[j] > 1.0 + doubleResolution ){
						if(solutions[j] < closestT){
							closestT = solutions[j];
							selectedObject = myScene->objects[i];
							itemFound = i;
						}
						break;
					}
				}
				else{
					if(solutions[j] < closestT){
						closestT = solutions[j];
						selectedObject = myScene->objects[i];
					}
					break;
				}
			}
		}
		if(selectedObject == NULL){
			if(recursionDepth == 0){
				return myScene->backgroundColor;
			}
			else{
				return Color();
			}
		}
		else{
			Vector intersectionPoint = initialPoint + direction * closestT;
			
			//Ambient lighting comes first
			Color totalColor = selectedObject->getColor(intersectionPoint) * myScene->ambientColor *
				selectedObject->ambienceConstant;

			//Reflected light next 	
			Vector normal = selectedObject->normal(intersectionPoint);
			if(selectedObject->contains(initialPoint)){
				normal = normal * -1.0;
			}
			Vector reflectedRayDirection = normal * (normal.dotProduct(direction)) * -2.0 + direction;
			Ray reflectedRay = Ray(intersectionPoint, reflectedRayDirection, currRefractionIndex, recursionDepth + 1);
			totalColor += reflectedRay.trace(myScene, itemFound) * selectedObject->reflectionConstant;

                        //Refracted light next
                        if (selectedObject->transparency > 0.0) {
                                double eta = currRefractionIndex / selectedObject->refractionIndex;
                                double cosI = -(normal.dotProduct(direction));
                                double cosT2 = 1.0 - eta*eta*(1.0 - cosI*cosI);
                                if (cosT2 > 0.0) {
                                        Vector refractedRayDirection = direction*eta + (normal * (eta*cosI - sqrt(cosT2)));
                                        Ray refractedRay = Ray(intersectionPoint, refractedRayDirection, selectedObject->refractionIndex, recursionDepth + 1);
                                        totalColor += refractedRay.trace(myScene, itemFound) * selectedObject->transparency;
                                }
                        }

			//Point source lighting last
			for(size_t i = 0; i < myScene->lights.size(); i++){
				Ray shadowRay = Ray(intersectionPoint, myScene->lights[i].position - intersectionPoint, currRefractionIndex, recursionDepth + 1);
				//Check if an object is blocking the way
				bool blocked = false;
				for(size_t j = 0; j < myScene->objects.size() && !blocked; j++){
					std::vector<double> solutions = myScene->objects[j]->intersection(shadowRay);
					double shadowT = (solutions.size() == 0) ? -1.0 : solutions[0];
					if((1.0 - doubleResolution > shadowT && shadowT >= doubleResolution)){
						blocked = true;
					}
				}
				if(!blocked && !selectedObject->contains(myScene->lights[i].position)){
					shadowRay.direction.normalize();
					
					Color diffuseColor = selectedObject->getColor(intersectionPoint) * myScene->lights[i].color *
						(normal.dotProduct(shadowRay.direction) * selectedObject->diffusionConstant);
					totalColor += diffuseColor;

					Vector view = direction * -1.0;
					view.normalize();
					Vector reflection = (normal * (normal.dotProduct(shadowRay.direction) * 2.0)) -
						shadowRay.direction;
					double rdotv = reflection.dotProduct(view);
					if(rdotv > doubleResolution){
						Color specularColor = myScene->lights[i].color;
						specularColor = specularColor * (selectedObject->specularConstant *
							pow(rdotv, selectedObject->specularExponent));
						totalColor += specularColor;
					}// If r.dotProduct(v) is negative, then there should be no specular lighting
				}
			}
			return totalColor;
		}
	}
}

